#pragma once
#ifndef COMMONS_H
#define COMMONS_H
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef unsigned char   BYTE;
typedef unsigned short  WORD;
typedef signed short    SIGNED_WORD;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef union ___PAIR_REGISTER {
    WORD w;
    struct {
        BYTE l;
        BYTE h;
    };
} PAIR_REGISTER;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct ___M6502 {
    PAIR_REGISTER   pc;
    BYTE            a;
    BYTE            x;
    BYTE            y;
    BYTE            sp;
    BYTE            status;
    int             ticks;
} M6502;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct ___PPU {
    int     scanline;
    int     mirroring;
    BYTE    open_bus;
    int     vram_latch;
    int     spr_overflow;
    int     spr_0_hit;
    int     in_vblank;
    BYTE    sram_address;
    WORD    vram_address;
    WORD    vram_address_increment;
    BYTE    vram_buffer;
    WORD    name_table_address;
    WORD    bkg_pattern_table_address;
    int     generate_nmi_on_vblank;
} PPU;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct ___CONSOLE {
    BYTE    ram[0x10000];
    BYTE    vram[0x4000];
    BYTE    sram[0x100];
    M6502*  m6502;
    PPU*    ppu;
} CONSOLE;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum {
    ABS      = 1,
    ABS_X    = 2,
    ABS_X_NC = 3,
    ABS_Y    = 4,
    ABS_Y_NC = 5,
    ACC      = 6,
    IMM      = 7,
    IND      = 8,
    IND_X    = 9,
    IND_Y    = 10,
    IND_Y_NC = 11,
    REL      = 12,
    ZP       = 13,
    ZP_X     = 14,
    ZP_Y     = 15,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum {
    FLAG_N = 0x80,
    FLAG_V = 0x40,
    FLAG_P = 0x20,
    FLAG_B = 0x10,
    FLAG_D = 0x08,
    FLAG_I = 0x04,
    FLAG_Z = 0x02,
    FLAG_C = 0x01,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum {
    INTERRUPT_NMI = 1,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum {
    MIRRORING_VERTICAL      = 1,
    MIRRORING_HORIZONTAL    = 2,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define BASE_TIME 1
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum {
    PPU_SPR_OVERFLOW    = 0x20,
    PPU_SPR_0_HIT       = 0x40,
    PPU_IN_VBLANK       = 0x80,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void    create_console();
void    reset_m6502();
void    load_file( const char[] );
void    run();
BYTE    read_memory( WORD );
void    write_memory( WORD, BYTE );
BYTE    fetch_opcode();
void    execute_opcode( BYTE );
void    stack_push( BYTE );
BYTE    stack_pop();
int     test_flag( int );
void    solve_flags_nz( BYTE );
WORD    solve_address( int );
WORD    address_abs();
WORD    address_abs_x();
WORD    address_abs_x_nc();
WORD    address_abs_y();
WORD    address_abs_y_nc();
WORD    address_imm();
WORD    address_ind();
WORD    address_ind_x();
WORD    address_ind_y();
WORD    address_ind_y_nc();
WORD    address_rel();
WORD    address_zp();
WORD    address_zp_x();
WORD    address_zp_y();
void    handle_interrupt( int );
void    nmi_interrupt();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void    opcode_adc( int );
void    opcode_and( int );
void    opcode_asl( int );
void    opcode_bcc();
void    opcode_bcs();
void    opcode_beq();
void    opcode_bit( int );
void    opcode_bmi();
void    opcode_bne();
void    opcode_bpl();
void    opcode_bvc();
void    opcode_bvs();
void    opcode_clc();
void    opcode_cld();
void    opcode_clv();
void    opcode_cmp( int );
void    opcode_cpx( int );
void    opcode_cpy( int );
void    opcode_dec( int );
void    opcode_dex();
void    opcode_dey();
void    opcode_eor( int );
void    opcode_inc( int );
void    opcode_inx();
void    opcode_iny();
void    opcode_jmp( int );
void    opcode_jsr();
void    opcode_lda( int );
void    opcode_ldx( int );
void    opcode_ldy( int );
void    opcode_lsr( int );
void    opcode_nop();
void    opcode_ora( int );
void    opcode_pha();
void    opcode_php();
void    opcode_pla();
void    opcode_plp();
void    opcode_ror( int );
void    opcode_rol( int );
void    opcode_rti();
void    opcode_rts();
void    opcode_sbc( int );
void    opcode_sec();
void    opcode_sed();
void    opcode_sei();
void    opcode_sta( int );
void    opcode_stx( int );
void    opcode_sty( int );
void    opcode_tax();
void    opcode_tay();
void    opcode_tsx();
void    opcode_txa();
void    opcode_txs();
void    opcode_tya();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void    reset_ppu();
void    run_scanline();
BYTE    read_ppu_2002();
BYTE    read_ppu_2007( WORD );
void    write_ppu_2000( BYTE );
void    write_ppu_2001( BYTE );
void    write_ppu_2003( BYTE );
void    write_ppu_2004( BYTE );
void    write_ppu_2005( BYTE );
void    write_ppu_2006( BYTE );
void    write_ppu_2007( BYTE );
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void    log_trace( BYTE );
BYTE    fetch_operand( int );
char*   solve_opcode_name( BYTE );
void    dump_memory( BYTE*, int );
void    trace_pattern_tables();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int     test_bit( BYTE, int );
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const char OPCODE_NAME[ 0x100 ][ 3 ] = {
//      0      1      2      3      4      5      6      7      8      9      A      B      C      D      E      F
/* 0 */ "UNK", "ORA", "UNK", "UNK", "UNK", "ORA", "ASL", "UNK", "PHP", "ORA", "ASL", "UNK", "UNK", "ORA", "ASL", "UNK",
/* 1 */ "BPL", "ORA", "UNK", "UNK", "UNK", "ORA", "ASL", "UNK", "CLC", "ORA", "UNK", "UNK", "UNK", "ORA", "ASL", "UNK",
/* 2 */ "JSR", "AND", "UNK", "UNK", "BIT", "AND", "ROL", "UNK", "PLP", "AND", "ROL", "UNK", "BIT", "AND", "ROL", "UNK",
/* 3 */ "BMI", "AND", "UNK", "UNK", "UNK", "AND", "ROL", "UNK", "SEC", "AND", "UNK", "UNK", "UNK", "AND", "ROL", "UNK",
/* 4 */ "RTI", "EOR", "UNK", "UNK", "UNK", "EOR", "LSR", "UNK", "PHA", "EOR", "LSR", "UNK", "JMP", "EOR", "LSR", "UNK",
/* 5 */ "BVC", "EOR", "UNK", "UNK", "UNK", "EOR", "LSR", "UNK", "UNK", "EOR", "UNK", "UNK", "UNK", "EOR", "LSR", "UNK",
/* 6 */ "RTS", "ADC", "UNK", "UNK", "UNK", "ADC", "ROR", "UNK", "PLA", "ADC", "ROR", "UNK", "JMP", "ADC", "ROR", "UNK",
/* 7 */ "BVS", "ADC", "UNK", "UNK", "UNK", "ADC", "ROR", "UNK", "SEI", "ADC", "UNK", "UNK", "UNK", "ADC", "ROR", "UNK",
/* 8 */ "UNK", "STA", "UNK", "UNK", "STY", "STA", "STX", "UNK", "DEY", "UNK", "TXA", "UNK", "STY", "STA", "STX", "UNK",
/* 9 */ "BCC", "STA", "UNK", "UNK", "STY", "STA", "STX", "UNK", "TYA", "STA", "TXS", "UNK", "UNK", "STA", "UNK", "UNK",
/* A */ "LDY", "LDA", "LDX", "UNK", "LDY", "LDA", "LDX", "UNK", "TAY", "LDA", "TAX", "UNK", "LDY", "LDA", "LDX", "UNK",
/* B */ "BCS", "LDA", "UNK", "UNK", "LDY", "LDA", "LDX", "UNK", "CLV", "LDA", "TSX", "UNK", "LDY", "LDA", "LDX", "UNK",
/* C */ "CPY", "CMP", "UNK", "UNK", "CPY", "CMP", "DEC", "UNK", "INY", "CMP", "DEX", "UNK", "CPY", "CMP", "DEC", "UNK",
/* D */ "BNE", "CMP", "UNK", "UNK", "UNK", "CMP", "DEC", "UNK", "CLD", "CMP", "UNK", "UNK", "UNK", "CMP", "DEC", "UNK",
/* E */ "CPX", "SBC", "UNK", "UNK", "CPX", "SBC", "INC", "UNK", "INX", "SBC", "NOP", "UNK", "CPX", "SBC", "INC", "UNK",
/* F */ "BEQ", "SBC", "UNK", "UNK", "UNK", "SBC", "INC", "UNK", "SED", "SBC", "UNK", "UNK", "UNK", "SBC", "INC", "UNK",
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const int OPCODE_CYCLES[ 0x100 ] = {
//      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
/* 0 */ 0, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0,
/* 1 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
/* 2 */ 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0,
/* 3 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
/* 4 */ 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0,
/* 5 */ 2, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0,
/* 6 */ 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0,
/* 7 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
/* 8 */ 0, 6, 2, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
/* 9 */ 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
/* A */ 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
/* B */ 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
/* C */ 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
/* D */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
/* E */ 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
/* F */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const long COLOR_PALLETE[ 64 ] = {
    0x757575, 0x271B8F, 0x0000AB, 0x47009F, 0x8F0077, 0xAB0013, 0xA70000, 0x7F0B00,
    0x432F00, 0x004700, 0x005100, 0x003F17, 0x1B3F5F, 0x000000, 0x000000, 0x000000,
    0xBCBCBC, 0x0073EF, 0x233BEF, 0x8300F3, 0xBF00BF, 0xE7005B, 0xDB2B00, 0xCB4F0F,
    0x8B7300, 0x009700, 0x00AB00, 0x00933B, 0x00838B, 0x000000, 0x000000, 0x000000,
    0xFFFFFF, 0x3FBFFF, 0x5F97FF, 0xA78BFD, 0xF77BFF, 0xFF77B7, 0xFF7763, 0xFF9B3B,
    0xF3BF3F, 0x83D313, 0x4FDF4B, 0x58F898, 0x00EBDB, 0x000000, 0x000000, 0x000000,
    0xFFFFFF, 0xABE7FF, 0xC7D7FF, 0xD7CBFF, 0xFFC7FF, 0xFFC7DB, 0xFFBFB3, 0xFFDBAB,
    0xFFE7A3, 0xE3FFA3, 0xABF3BF, 0xB3FFCF, 0x9FFFF3, 0x000000, 0x000000, 0x000000
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CONSOLE* emu;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif